#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include "SDL.h"
#include "SDL_image.h"
#include "location.h"
#include "character.h"
#include "tree.h"
#include "item.h"
#include "mapitem.h"
#include <sstream>
#include <boost/archive/binary_oarchive.hpp>                               
#include <boost/archive/binary_iarchive.hpp>

using namespace std;

Tree::Tree()
{
        // Root-pointer is initialized to zero
	root_ = 0;
}


void Tree::add(Location *&n) throw(tree_error)
{
	add(root_, n);
}

void Tree::add(Location *&p, Location *&n) throw(tree_error)
{
	
	if(p == 0)
	{
		p = new Location;

		p->right_node = 0;
		p->left_node = 0;

		p->setName(n->getName());
		p->setDescription(n->getDescription());

		string tempList = n->getRestrictions();
		string tempSave = "";
		istringstream is;
		is.str(tempList);
		while(is >> tempSave)
		{
		       p->addRestriction(tempSave);
		}
		

		for(int i = 0; i < n->itemsObj_.size() ; i++)
		{
		       p->addItem(n->itemsObj_[i]);
		}

		// Copy the mapItems
		for(int y = 0; y < 44; y++)
		{
		       for(int x = 0; x < 17; x++)
		       {
			      p->map_[x][y] = n->map_[x][y];
		       }
		}

		tempList = n->getLocations();
		tempSave = "";
		is.clear();
		is.str(tempList);
		while(is >> tempSave)
		{
		       p->addLocation(tempSave);
		}
		
	}
	else if(n->getName() < p->getName())
	{
	       add(p->left_node, n);
	}
	else if(n->getName() > p->getName())
	{
	       add(p->right_node, n);
	}
	else
	{
		throw tree_error("Could not add node to tree!");
	}	
}

void Tree::remove(std::string s) throw (tree_error)
{
	remove(root_, s);
}

void Tree::remove(Location *&p, std::string &s) throw(tree_error)
{
	if(p == 0)
	{
		throw tree_error("Trying to remove an empty node");
	}
	else if(s < p->getName())
	{
	       remove(p->left_node, s);
	}
	else if(s > p->getName())
	{
	       remove(p->right_node, s);
	}
	else if(p->left_node != 0 && p->right_node != 0)
	{
		// The node has children
		Location *temp;
		temp = find_min(p->right_node);
		
		p->setName(temp->getName());
		p->setDescription(temp->getDescription());

		string tempList = temp->getRestrictions();
		string tempSave = "";
		istringstream is;
		is.str(tempList);
		while(is >> tempSave)
		{
		       p->addRestriction(tempSave);
		}

		// Copy the items
		for(int i = 0; i < temp->itemsObj_.size() ; i++)
		{
		       p->addItem(temp->itemsObj_[i]);
		}

		// Copy the mapItems
		for(int y = 0; y < 44; y++)
		{
		       for(int x = 0; x < 17; x++)
		       {
			      p->map_[x][y] = temp->map_[x][y];
		       }
		}

		tempList = temp->getLocations();
		tempSave = "";
		is.clear();
		is.str(tempList);
		while(is >> tempSave)
		{
		       p->addLocation(tempSave);
		}

		string tempRemoveString =  temp->getName();
		remove(temp, tempRemoveString);
	}
	else
	{
		Location *garbage;
		garbage = p;
		if(p->right_node != 0)
		{
		       p = p->right_node;
		}
		else if(p->left_node != 0)
		{
		       p = p->left_node;
		}
		else
		{
			p = 0;
		}

		delete garbage;
	}
}

Location *Tree::find_min(Location *p) throw(tree_error)
{
	if(p == 0)
	{
		throw tree_error("Noden kan ej finnas!");
	}
	if(p->left_node == 0)
	{
		return p;
	}

	return find_min(p->right_node);
}

Location *Tree::find_l(std::string name)
{
	return find_l(name, root_);
}

Location *Tree::find_l(std::string &name, Location *p)
{
	Location *t = 0;

	if(p == 0)
		return 0;

	if(p->getName() == name)
	{
		t = p;
	}
	else
	{
		if(p->left_node != 0)
		{
		        t = find_l(name, p->left_node);
		}
		
		if(p->right_node != 0 && t == 0)
		{
			t = find_l(name, p->right_node);
		}
		
	}
	
	return t;
}

Location * Tree::find_exit(Location *&c, std::string &location)
{
       string tempLocation = "";
       istringstream readLocation;
       readLocation.str(c->getLocations());

       while(readLocation >> tempLocation)
       {
	      if(tempLocation == location)
		     return find_l(location);

	      tempLocation = "";

       }

	return 0;	
}

Tree::~Tree()
{
	// Tom
}

void Tree::save(string filename)
{
	filename = "adventure2/maps/" + filename + ".adv";

	// Clear the files
	const char* file = filename.c_str();
	fstream savetofile(file, ios::out|ios::binary|ios::trunc);	
	savetofile.close();
	

	// Save the entire tree with serialization
	{
                ofstream ofs(file);
                boost::archive::binary_oarchive oa(ofs);
                oa << root_;
	}
	
	cout << "Map saved." << endl;      
}

Location* Tree::load(string filename)
{
       emptytree();

       filename = "adventure2/maps/" + filename + ".adv";
       string mapinfo = filename + "i";
       const char* file = filename.c_str();

       // Load the entire map with serializtion
       {
                ifstream ifs(file);
                boost::archive::binary_iarchive ia(ifs);
                ia >> root_;
       }

       return root_;
}

void Tree::emptytree()
{
	if(root_ != 0)
	{
		if(root_->left_node != 0)
		{
			emptytree(root_->left_node);
		}
		
		if(root_->right_node != 0)
		{
			emptytree(root_->right_node);
		}
		
		root_ = 0;
	}

	cout << "Map in memory cleared." << endl;
}

void Tree::emptytree(Location* p)
{
	if(p->left_node != 0)
	{
		emptytree(p->left_node);
	}
	
	if(p->right_node != 0)
	{
		emptytree(p->right_node);
	}
	
	delete p;
}

void Tree::print_tree()
{
	if(root_ != 0)
	{
	       cout << root_->getName() << endl;
		
		if(root_->left_node != 0)
		{
			print_tree(root_->left_node);
		}
		
		if(root_->right_node != 0)
		{
			print_tree(root_->right_node);
		}
	}
}

void Tree::print_tree(Location *p)
{
	
       cout << p->getName() << endl;
	
	if(p->left_node != 0)
	{
		print_tree(p->left_node);
	}
	
	if(p->right_node != 0)
	{
		print_tree(p->right_node);
	}
	
}
